using System;
using System.Collections.Generic;
using System.Text;
using Mpi.Common.Collections;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Linq;

namespace Xeml.Document
{
    /// <summary>
    /// All about sample identification.
    /// </summary>
    public class SidMapping : XemlAnnotatableObject, INotifyPropertyChanged
    {
        #region Fields

        private int _sampleId;
        private ForeignKeyMap _keyMap;
        //private string _providerName;

        #endregion

        #region Properties

        /// <summary>
        /// The sample ID to be mapped
        /// </summary>
        public int SampleId
        {
            get { return _sampleId; }

        }



        /// <summary>
        /// A collection of subkeys used for the primary key of an sample of a specific sample provider.
        /// </summary>
        public ForeignKeyMap ForeignKeyMap
        {
            get { return _keyMap; }
            set 
            {
                if (_keyMap != value)
                {
                    if(_keyMap != null)
                        _keyMap.CollectionChanged -=new EventHandler(_keyMap_CollectionChanged);
                    _keyMap = value;
                    if (_keyMap != null)
                        _keyMap.CollectionChanged += new EventHandler(_keyMap_CollectionChanged);

                    OnPropertyChanged("ForeignKeyMap");
                }
            }
        }


        #endregion

        #region Constructors
        /// <summary>
        /// The standard constructor.
        /// </summary>
        public SidMapping(int sampleId)
        {
            _sampleId = sampleId;
            this._keyMap = new ForeignKeyMap();
            _keyMap.CollectionChanged += new EventHandler(_keyMap_CollectionChanged);
        }

        void _keyMap_CollectionChanged(object sender, EventArgs e)
        {
            OnPropertyChanged("ForeignKeyMap");
        }

        //public SidMapping(int sampleId, string provider) : this(sampleId)
        //{

        //    _providerName = provider;

        //}

        #endregion

        #region Methods
        /// <summary>
        /// Converts the current object to it's string representation.
        /// </summary>
        /// <returns>A string representation.</returns>
        //public override string ToString()
        //{
        //    return (string)this;
        //    StringBuilder sb = new StringBuilder();
        //    foreach (string s in this._keyMap.Values)
        //    {
        //        sb.Append(s);
        //        sb.Append("-");
        //    }
        //    if (sb.Length > 0)
        //    {
        //        sb.Remove(sb.Length - 1, 1);
        //        //sb.Append(":");
        //        //sb.Append(this.technicalReplicateNumber);
        //        // sb.AppendFormat("@{0}", this.ProviderName);
        //    }
        //    return sb.ToString();
        //}

        /// <summary>
        /// Calculates an hashcode for the current object.
        /// </summary>
        /// <returns>An unique hash code.</returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Compares the current object with another object.
        /// </summary>
        /// <param name="obj">The object for comparision.</param>
        /// <returns>A value that indicates whether the objects are equal or not.</returns>
        public override bool Equals(object obj)
        {
            if (obj is SidMapping && obj != null)
            {
                SidMapping s = obj as SidMapping;


                foreach (string k in this._keyMap.Keys)
                {
                    if (s.ForeignKeyMap.ContainsKey(k))
                    {
                        if (this._keyMap[k] != s.ForeignKeyMap[k])
                            return false;
                    }
                    else
                        return false;
                }

                foreach (string k in s.ForeignKeyMap.Keys)
                {
                    if (this.ForeignKeyMap.ContainsKey(k))
                    {
                        if (this._keyMap[k] != s.ForeignKeyMap[k])
                            return false;
                    }
                    else
                        return false;
                }
                return true;

            }
            else return false;
        }
        #endregion

      
        private DataMappings _collection;
        public DataMappings Collection
        {
            get
            {
                return this._collection;
            }
            set
            {
                if (value != _collection)
                {
                    this._collection = value;
                }

            }
        }

   

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        public virtual void OnPropertyChanged(string prop)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
            
        }
        #endregion
    }



    public class ForeignKeyMap : IEnumerable<ForeignKeyValue>, ICloneable
    {
        private List<ForeignKeyValue> _innerList;


        public ForeignKeyMap()
        {
            _innerList = new List<ForeignKeyValue>();
        }

        public ForeignKeyMap(ForeignKeyMap copy)
        {
            _innerList = new List<ForeignKeyValue>(copy);
        }

        public ReadOnlyCollection<string> Keys
        {
            get 
            {
                return new ReadOnlyCollection<string>(this.Select(x=>x.Key).ToList());
            }
        }

        public int Count
        {
            get { return _innerList.Count; }
        }

        public string this[string key]
        {
            get
            {
                if (string.IsNullOrEmpty(key))
                    throw new ArgumentNullException();
                ForeignKeyValue fkv = FindForreignKeyValue(key);
                if (fkv != null)
                    return fkv.Value;
                else
                    throw new ArgumentException("UnknownSample key");
            }
            set
            {
                if (string.IsNullOrEmpty(key))
                    throw new ArgumentNullException();
                ForeignKeyValue fkv = FindForreignKeyValue(key);
                if (fkv != null)
                    fkv.Value = value;
                else
                    throw new ArgumentException("UnknownSample key");
            }
        }

        private ForeignKeyValue FindForreignKeyValue(string key)
        {
            ForeignKeyValue fkv = this._innerList.Find(delegate(ForeignKeyValue x) { return x.Key == key; });
            return fkv;
        }



        public void Add(string key, string val) 
        {
            Add(key, val, false);
        }

        public void Add(string key, string val, bool overwriteIfExist)
        {


            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException();

            if (ContainsKey(key))
            {
                if (overwriteIfExist)
                {
                    this[key] = val;
                }
                else
                    throw new ArgumentException("A value with the given key already exist");
            }
            else
            {
                ForeignKeyValue fkv = new ForeignKeyValue { Key = key, Value = val };

                _innerList.Add(fkv);
                fkv.PropertyChanged += new PropertyChangedEventHandler(fkv_PropertyChanged);
            }
            OnCollectionChanged();
        }

        public void Remove(string key)
        {
           
                ForeignKeyValue toRem = FindForreignKeyValue(key);
                if (toRem != null)
                {
                    toRem.PropertyChanged -= new PropertyChangedEventHandler(fkv_PropertyChanged);
                    _innerList.Remove(toRem);
                    OnCollectionChanged();
                }
      
        }

        public void Clear()
        {
            foreach (var fkv in this)
            {
                fkv.PropertyChanged -= new PropertyChangedEventHandler(fkv_PropertyChanged);
            }
            _innerList.Clear();
            OnCollectionChanged();
        }

        void fkv_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            OnCollectionChanged();
        }

        public bool ContainsKey(string key)
        {
            return Keys.Contains(key);
          //  return this._innerList.Find(delegate(ForeignKeyValue x) { return x.Key == key; }) != null;
        }
	
        public static implicit operator string(ForeignKeyMap fmp)
        {
           
            if (fmp == null)
                return null;
            StringBuilder sb = new StringBuilder();
            foreach (var kvp in fmp)
            {
                sb.AppendFormat("{0}={1};", kvp.Key, kvp.Value);
            }
            if (sb.Length > 0 && sb[sb.Length-1] != '=')
                sb.Remove(sb.Length - 1, 1);
            return sb.ToString();
        }

        public static implicit operator ForeignKeyMap(string fmpString)
        {

            ForeignKeyMap fkm = new ForeignKeyMap();
            if (String.Empty == fmpString)
                return fkm;
            else if (fmpString == null)
                return fkm;
            else
            {
                string[] x = fmpString.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (string kvpstring in x)
                {
                    string[] y = kvpstring.Split("=".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                    if (y.Length == 2)
                    {
                        fkm.Add(y[0].Trim(), y[1].Trim());
                    }
                    else if (y.Length == 1)
                    {
                        fkm.Add(y[0].Trim(), "");
                    }
                }
                return fkm;
            }


        }

        public event EventHandler CollectionChanged;
        protected virtual void OnCollectionChanged()
        {
            if (CollectionChanged != null)
                CollectionChanged(this, new EventArgs());
        }

        public override string ToString()
        {
            return (string)this;
        }

        #region IEnumerable<ForeignKeyValue> Members

        public IEnumerator<ForeignKeyValue> GetEnumerator()
        {
            return _innerList.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _innerList.GetEnumerator();
        }

        #endregion

        #region ICloneable Members

        public object Clone()
        {
            ForeignKeyMap clone = new ForeignKeyMap();
            foreach (var item in this._innerList)
            {
                clone.Add(item.Key, item.Value);
            }
            return clone;
        }

        #endregion
    }

    public class ForeignKeyValue : INotifyPropertyChanged
    {
        private string _key;

        public string Key
        {
            get { return _key; }
            set 
            {
                if (_key != value)
                {
                    _key = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Key"));
                }
            }
        }

        private string _val;

        public string Value
        {
            get { return _val; }
            set 
            { 
            
                if (_val != value)
                {
                    _val = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Values"));
                }
            }
        }



        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
